package cn.net.yato.picture.config.shiro;

import cn.net.yato.picture.biz.SysBiz;
import cn.net.yato.picture.constant.CommParams;
import cn.net.yato.picture.entity.Permission;
import cn.net.yato.picture.entity.Role;
import cn.net.yato.picture.entity.User;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName MyRealm
 * @Description 自定义realm
 * @Author yato
 * @Date 2020/01/11 08:40
 * @Version 1.0
 */
public class UserNamePasswordRealm extends AuthorizingRealm {

    @Resource
    private SysBiz sysBiz;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    private static final int MAX_LOGIN_COUNT =  5;
    public  static  String SHIRO_LOGIN_COUNT = "shiro_login_count_";
    public  static  String SHIRO_IS_LOCK = "shiro_is_lock_";
    public  static  String SHIRO_LOCK = "LOCK";

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return getAuthorizationInfo(principals);
    }

    @Override
    public AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        List<String> listRoleNames = new ArrayList<>();
        User user = (User) principals.getPrimaryPrincipal();
        for (Permission permission : user.getPermissions()) {
            authorizationInfo.addStringPermission(permission.getPerms());
        }
        for (Role role : user.getRoles()) {
            listRoleNames.add(role.getPerms());
            for (Permission permission : role.getPermissions()) {
                authorizationInfo.addStringPermission(permission.getPerms());
            }
        }
        authorizationInfo.addRoles(listRoleNames);
        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        String userName = usernamePasswordToken.getUsername();
        String passWord = new String(usernamePasswordToken.getPassword());
        User user = sysBiz.loginBiz(userName);
        System.out.println(user);
        ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
        opsForValue.increment(SHIRO_LOGIN_COUNT  + userName, 1);
        if (Integer.parseInt(opsForValue.get(SHIRO_LOGIN_COUNT + userName)) > MAX_LOGIN_COUNT) {
            opsForValue.set(SHIRO_IS_LOCK + userName, SHIRO_LOCK);
            stringRedisTemplate.expire(SHIRO_IS_LOCK + userName, 1, TimeUnit.HOURS);
            opsForValue.set(SHIRO_LOGIN_COUNT + userName, "0");
        }
        if (SHIRO_LOCK.equals(opsForValue.get(SHIRO_IS_LOCK + userName))) {
            throw new LockedAccountException("由于输入错误次数大于 5 次,帐号 1 小时内已经禁止登录! ");
        }
        if (user == null){
            throw new UnknownAccountException("账号不存在!");
        } else if (user.getStatus()==0) {
            throw new DisabledAccountException  ("账号已停用!");
        } else {
            Md5Hash md5Hash = new Md5Hash(passWord,user.getSalt(), CommParams.USER_PWD_HASHITERATIONS);
            if(!(md5Hash.toString().equals(user.getPassWord()))){
                throw new IncorrectCredentialsException("密码输入错误了呢!");
            }
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user,
                passWord,
                ByteSource.Util.bytes(user.getSalt()),
                getName()
        );
        opsForValue.set(SHIRO_LOGIN_COUNT + userName, "0");
        return authenticationInfo;
    }


    /**
     * 用来判断是否使用当前的 realm
     * @param authenticationToken 传入的token
     * @return true就使用，false就不使用
     */
    @Override
    public boolean supports(AuthenticationToken authenticationToken){
        return authenticationToken instanceof UsernamePasswordToken;
    }

}
